#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <math.h>

#include "kernel.h"
// oslib
#include "os.h"
#include "osfile.h"
#include "hourglass.h"
#include "osspriteop.h"
#include "wimp.h"

#include "hbp10gm.h"
#include "midifile.h"


#define MODE_SINGLETASK       0
#define MODE_MULTITASK        1
#define MODE_WIMP             2

#define OUTPUT_HBP10GM        (1<<0)
#define OUTPUT_MIDI           (1<<1)
#define OUTPUT_SCREEN         (1<<2)

#define MIDI_Init             (0x404c0 + 34)
#define MIDI_FastClock        (0x404c0 + 33)
#define MIDI_TxCommand        (0x404c0 + 10)

#define ICON_PLAY             0
#define ICON_STOP             1
#define ICON_PAUSE            2
#define ICON_TITLE            3
#define ICON_VOLUME           4
#define ICON_DURATION         6
#define ICON_POSITION         7

#define ICON_VOLUME_X         192
#define ICON_VOLUME_Y         76
#define VU_METER_X            270
#define VU_METER_Y            76

#define DRAGTYPE_IDLE         0
#define DRAGTYPE_VOLUME       1

#define STOPPED               0
#define PLAYING               1
#define PAUSED                2
#define WAITINGFOREND         3

#define NOTEOFF                 0x80
#define NOTEON                  0x90
#define AFTERTOUCH              0xa0
#define CONTROLCHANGE           0xb0
#define PROGRAMCHANGE           0xc0
#define CHANNELPRESSURE         0xd0
#define PITCHWHEEL              0xe0
#define SYSTEM                  0xf0


#define MAXEVENTS             100

typedef struct midievent {
  int ms;
  unsigned char channel, event, arg1, arg2;
} midievent;

static midievent events[MAXEVENTS];


static int quit, output, playing, mode;
static int midifilesize, midifileduration, midifileposition;
static char *midifilebuffer;
static char templatedata[1000];
static wimp_w main_window;
static osspriteop_area *spritearea;
static unsigned int volume = 256;
static int dragtype = DRAGTYPE_IDLE;
static int last_x_position, last_y_position;
static int free_in_midi_scheduler = 0;
static int time_of_last_command = 0;

static int vumeter = 0;
static char vusprite[4];

static os_error err_window_not_found = { 1, "Window not found" };

static char *load_midi_file(char *midifile, int *size);
static void poll_multitask(void);
static int poll_midi(void);
static int playevent(int event, int milliseconds);
static int start_playback(void);
static void report_error(os_error *err, int die);
static void all_notes_off(void);
static void redraw_play_window(wimp_draw *draw, bool *more);
static void set_volume(int v);
static void set_file_title(char *name);
static void set_icon(wimp_i i, int pressed);
static void stop_playback(int notesoff);
static void pause_playback(void);
static void unpause_playback(void);
static void set_vu_meter(int vu);
static void set_file_position(int ms, int dur);
static char *get_event_string(int ms, int event, char *b);



void usage(char *err) {

  if (*err)  fprintf(stderr, "%s\n\n", err);
  fprintf(stderr, "*PlayMIDI 0.09 - by Henrik Bjerregaard Pedersen\n\n");
  fprintf(stderr, "Usage:   *PlayMIDI [-o <device>] [-mode <mode>] <midifile>\n");
  exit(-1);
}


int main(int argc, char *argv[]) {

  char *midifile;
  int arg;

  // reset stuff
  midifile = NULL;
  mode = MODE_SINGLETASK;
  quit = 0;
  output = 0;
  playing = STOPPED;

  // parse arguments
  if (argc < 2)  usage("Too few arguments");

  for (arg = 1; arg < argc; arg++) {
    if (strcmp(argv[arg], "-mode") == 0) {
      arg++;
      if (strcmp(argv[arg], "SINGLETASK") == 0)
        mode = MODE_SINGLETASK;
      else if (strcmp(argv[arg], "MULTITASK") == 0)
        mode = MODE_MULTITASK;
      else if (strcmp(argv[arg], "WIMP") == 0)
        mode = MODE_WIMP;

    } else if (strcmp(argv[arg], "-o") == 0) {
      arg++;
      if (strcmp(argv[arg], "HBP10GM") == 0)
        output |= OUTPUT_HBP10GM;
      else if (strcmp(argv[arg], "MIDI") == 0)
        output |= OUTPUT_MIDI;
      else if (strcmp(argv[arg], "SCREEN") == 0)
        output |= OUTPUT_SCREEN;
      else
        usage("Unknown output-device specified");

    } else if (arg == argc-1)
      midifile = argv[arg];
  }

  // argument-dependencies
  if (output == 0)              output = OUTPUT_HBP10GM;

  // we need a midifile except if in wimp-mode
  if ((!midifile) && (mode != MODE_WIMP))   usage("No midifile specified");

  // if there is a midifile, load it
  if (midifile) {
    midifilebuffer = load_midi_file(midifile, &midifilesize);
    if (!midifilebuffer) {
      if (mode != MODE_WIMP)   usage("Failed to load midi file");
      midifile = NULL;
    }
  }

  // init the wimp if necessary
  if ((mode == MODE_MULTITASK) || (mode == MODE_WIMP)) {
    int msgs[] = { message_DATA_LOAD, message_QUIT };

    if (xwimp_initialise(310, "MIDI Player", (wimp_message_list *)&msgs, NULL, NULL))
      exit(-1);

    if (mode == MODE_WIMP) {
      wimp_window_state state;
      wimp_window *tw;
      int used, found, size;
      char temp[1000], *td;
      os_error *err;
      bits ftype;

      // load sprites
      if (xosfile_read_stamped_no_path("<PlayMIDI$Dir>.Sprites",
                       NULL, NULL, NULL, &size, NULL, &ftype))  exit(-1);
      if (ftype != 0xff9)  exit(-1);
      spritearea = malloc(size+16);
      if (!spritearea)  exit(-1);
      spritearea->size = size+16;
      spritearea->sprite_count = 0;
      spritearea->first = 16;
      spritearea->used = 16;
      xosspriteop_load_sprite_file(osspriteop_USER_AREA, spritearea, "<PlayMIDI$Dir>.Sprites");

      // open the templates-file
      err = xwimp_open_template("<PlayMIDI$Dir>.Templates");
      if (err)          exit(-1);

      td = templatedata;
      tw = (wimp_window *)temp;
      // load Main window
      err = xwimp_load_template(tw, td, templatedata+1000,
                                (font_f *)-1, "Control", 0, NULL, &used, &found);
      if (err)          report_error(err, 1);
      if (!found)       report_error(&err_window_not_found, 1);
      tw->sprite_area = spritearea;
      err = xwimp_create_window(tw, &main_window);
      if (err)          report_error(err, 1);

      td = (char *)used;

      // close templates-file
      xwimp_close_template();
/*
      // put icon on the iconbar
      icon.w = wimp_ICON_BAR_RIGHT;
      icon.icon.extent.x0 = 0;
      icon.icon.extent.y0 = 0;
      icon.icon.extent.x1 = 68;
      icon.icon.extent.y1 = 68;
      icon.icon.flags = 0x3002;
      strcpy(icon.icon.data.sprite, "!PlayMIDI");
      err = xwimp_create_icon(&icon, NULL);
      if (err)          report_error(err, 1); */

      state.w = main_window;
      xwimp_get_window_state(&state);
      xwimp_open_window((wimp_open *)&state);
      set_file_title("");
      set_vu_meter(0);
      set_file_position(0, 0);
      set_file_position(0, 1);
    }
  }

  // make sure we tidy the synth when we exit
  atexit(all_notes_off);

  // if we've loaded a midifile, play it
  if (midifile) {
    fprintf(stderr, "Playing '%s'\n", midifile);
    if (start_playback() != MIDI_PLAYING) {
      if (mode == MODE_WIMP) {
      } else
        usage("Failed to start playback");
    }
  }

  while (!quit) {
    if (mode == MODE_SINGLETASK) {
      if (poll_midi() != MIDI_PLAYING)  quit = 1;
    } else {
      poll_multitask();
    }
  }

  if (playing)  stop_playback(1);
  if (mode != MODE_SINGLETASK)  xwimp_close_down(0);
}



void poll_multitask() {

  wimp_block block;
  wimp_event_no reason;

  if (dragtype != DRAGTYPE_IDLE)
    xwimp_poll(0, &block, NULL, &reason);
  else {
    os_t t;

    xos_read_monotonic_time(&t);
    xwimp_poll_idle(0, &block, t+5, NULL, &reason);
  }

  switch (reason) {
  case wimp_NULL_REASON_CODE:
    if (mode == MODE_WIMP) {
      if (output & OUTPUT_HBP10GM) {
        int vu;

        vu = hbp10gm_read_output_volume();
        if (vu < 0)
          vu = 0;
        else
          vu &= 255;

        if (vu != vumeter)   set_vu_meter(vu);

        if ((playing == PLAYING) || (playing == WAITINGFOREND)) {
          int first, tps;

          hbp10gm_scheduler_info(NULL, &first, NULL, NULL, NULL, &tps);
          first = (first*1000)/tps;
          if (first/1000 != midifileposition/1000)  set_file_position(first, 0);
        }
      }
      if (output & OUTPUT_MIDI) {
        if ((playing == PLAYING) || (playing == WAITINGFOREND)) {
          _kernel_swi_regs r;

          r.r[0] = -1;
          _kernel_swi(MIDI_FastClock, &r, &r);
          if (r.r[1]/1000 != midifileposition/1000)  set_file_position(r.r[1], 0);
        }
      }
    }
    if (playing == PLAYING) {
      int status;
      status = poll_midi();
      if (status != MIDI_PLAYING) {
        fprintf(stderr, "End of file\n");
        if (mode == MODE_WIMP) {
          if (status == MIDI_FINISHED)
            playing = WAITINGFOREND;
          else
            stop_playback(0);
        } else
          quit = 1;
      }
    } else if (playing == WAITINGFOREND) {
      int done;

      done = 0;
      if (output & OUTPUT_HBP10GM) {
        int freespace, size;
        hbp10gm_scheduler_info(&freespace, NULL, NULL, &size, NULL, NULL);
        if (freespace >= size-1)  done = 1;
      }
      if (output & OUTPUT_MIDI) {
        _kernel_swi_regs r;

        r.r[0] = -1;
        _kernel_swi(MIDI_FastClock, &r, &r);
        if (r.r[1] >= time_of_last_command)   done = 1;
      }
      if (done)   stop_playback(0);
    }
    if (dragtype != DRAGTYPE_IDLE) {
      wimp_pointer ptr;

      xwimp_get_pointer_info(&ptr);
      if (ptr.pos.x != last_x_position || ptr.pos.y != last_y_position) {
        last_x_position = ptr.pos.x;
        last_y_position = ptr.pos.y;

        switch (dragtype) {
        case DRAGTYPE_VOLUME:
          {
            wimp_window_state state;
            int x, y, v, dB;

            state.w = main_window;
            xwimp_get_window_state(&state);
            x = ptr.pos.x - (state.visible.x0 - state.xscroll + ICON_VOLUME_X + 33);
            y = ptr.pos.y - (state.visible.y1 - state.yscroll + ICON_VOLUME_Y + 33);

            if (x == 0 && y > 0)
              v = 90;
            else if (x == 0 && y < 0)
              v = -90;
            else if (x >= 0 && y == 0)
              v = 0;
            else if (x < 0 && y == 0)
              v = 180;
            else {
              v = (int)(57.295*atan((double)y/(double)x));
              if (x < 0)  v += 180;
            }
            dB = -(v + 50)*60/280;
            if (dB < -60)  dB = -60;
            if (dB > 0)    dB = 0;
            v = (int)pow(10.0, (double)(60+dB)/20.0);
            set_volume(v);
          }
          break;
        }
      }
    }
    break;

  case wimp_REDRAW_WINDOW_REQUEST:
    if (block.redraw.w == main_window) {
      bool more;

      xwimp_redraw_window(&block.redraw, &more);
      redraw_play_window(&block.redraw, &more);
    }
    break;

  case wimp_OPEN_WINDOW_REQUEST:
    if (block.open.w == main_window)   xwimp_open_window(&block.open);
    break;

  case wimp_CLOSE_WINDOW_REQUEST:
    if (block.open.w == main_window)   quit = 1;
    break;

  case wimp_MOUSE_CLICK:
    if (block.pointer.w == main_window) {
      switch (block.pointer.i) {
      case ICON_VOLUME:
        if (playing) {
          wimp_drag drag;
          wimp_window_state state;
          int x0, y0;

          // start dragging
          state.w = main_window;
          xwimp_get_window_state(&state);
          x0 = state.visible.x0 - state.xscroll;
          y0 = state.visible.y1 - state.yscroll;

          drag.w = main_window;
          drag.type = 7;
          drag.initial.x0 = 0;
          drag.initial.y0 = 0;
          drag.initial.x1 = 0;
          drag.initial.y1 = 0;
          drag.bbox.x0 = x0 + ICON_VOLUME_X;
          drag.bbox.y0 = y0 + ICON_VOLUME_Y;
          drag.bbox.x1 = x0 + ICON_VOLUME_X + 68;
          drag.bbox.y1 = y0 + ICON_VOLUME_Y + 68;
          dragtype = DRAGTYPE_VOLUME;
          xwimp_drag_box(&drag);
          last_x_position = last_y_position = -10000;
        }
        break;
      case ICON_PLAY:
        if (playing == PAUSED)     unpause_playback();
        break;
      case ICON_STOP:
        if (playing)      stop_playback(1);
        break;
      case ICON_PAUSE:
        if (playing == PLAYING)
          pause_playback();
        else if (playing == PAUSED)
          unpause_playback();
        break;
      }
    }
    break;

  case wimp_USER_DRAG_BOX:
    switch (dragtype) {
    case DRAGTYPE_VOLUME:
      break;
    }
    dragtype = DRAGTYPE_IDLE;
    break;

  case wimp_USER_MESSAGE:
  case wimp_USER_MESSAGE_RECORDED:
  case wimp_USER_MESSAGE_ACKNOWLEDGE:
    {
      wimp_message *msg;
      msg = &block.message;

      switch (msg->action) {
      case message_QUIT:
        quit = 1;
        break;

      case message_DATA_LOAD:
        if (mode == MODE_WIMP) {
          if (msg->data.data_xfer.w == main_window &&
              msg->data.data_xfer.file_type == 0xfd4) {
            stop_playback(1);
            midifilebuffer = load_midi_file(msg->data.data_xfer.file_name, &midifilesize);
            if (midifilebuffer) {
              fprintf(stderr, "Playing '%s'\n", msg->data.data_xfer.file_name);
              set_file_title(msg->data.data_xfer.file_name);
              start_playback();
              set_icon(ICON_PLAY, 1);
            }
          }
        }
        break;
      }
    }
    break;
  }
}



char *load_midi_file(char *midifile, int *size) {

  FILE *fh;
  char *buffer;

  fh = fopen(midifile, "rb");
  if (!fh)  return NULL;
  fseek(fh, 0, SEEK_END);
  *size = (int)ftell(fh);
  fseek(fh, 0, SEEK_SET);
  buffer = malloc(*size+1);
  if (buffer)  fread(buffer, 1, *size, fh);
  fclose(fh);

  return buffer;
}


int poll_midi() {

  int position, n, maxtime;

  maxtime = 0x7f000000;

  if (output & OUTPUT_HBP10GM) {
    int last, tick, tps, ms;

    if (hbp10gm_scheduler_info(NULL, NULL, &last, NULL, &tick, &tps))   return MIDI_FAILED;
    if ((last-tick)*1000/tps > 1000)  return MIDI_PLAYING;
    ms = last*1000/tps + 500;
    if (maxtime > ms)  maxtime = ms;
    n = 100;
  }
  if (output & OUTPUT_MIDI) {
    n = free_in_midi_scheduler;
    if (n > 100)  n = 100;
    maxtime = 500;
  }
  return midifile_poll(&position, playevent, n, maxtime);
}


// called from midifile.c
int playevent(int event, int milliseconds) {

  int rval;

  rval = MIDI_PLAYING;

  if (output & OUTPUT_HBP10GM) {
    int available;

    available = hbp10gm_schedule(event, 0, milliseconds);
    if (available <= 2)   rval = MIDI_BUFFER_FULL;
  }
  if (output & OUTPUT_MIDI) {
    _kernel_swi_regs r;

    r.r[0] = (event>>8) & 0x00ffffff;
    r.r[1] = milliseconds;
    _kernel_swi(MIDI_TxCommand, &r, &r);
    if (r.r[0] >= 0)
      free_in_midi_scheduler = r.r[0];
    else if (r.r[0] == -1)
      rval = MIDI_BUFFER_FULL;
  }
  if (output & OUTPUT_SCREEN) {
    char temp[64];
    if (mode == MODE_SINGLETASK)
      printf(get_event_string(milliseconds, event, temp));
    else
      fprintf(stderr, get_event_string(milliseconds, event, temp));
  }
  time_of_last_command = milliseconds;
  return rval;
}


char *get_event_string(int ms, int event, char *b) {

  static char *notes[12] = { " C", "C#", " D", "D#", " E", " F",
                               "F#", " G", "G#", " A", "A#", " B" };
  int n0, n1;

  sprintf(b, "%08d %02d ", ms, 1+((event>>8) & 0x0f));

  n0 = (event>>16) & 0x7f;
  n1 = (event>>24) & 0x7f;

  switch ((event>>8) & 0xf0) {
  case NOTEOFF:
    sprintf(b+12, "NOTE OFF        %s%d (%2d)  (%02d)\n", notes[n0%12], n0/12, n0+1, n1);
    break;
  case NOTEON:
    if (n1)
      sprintf(b+12,"NOTE ON         %s%d (%2d)  (%02d)\n", notes[n0%12], n0/12, n0+1, n1);
    else
      sprintf(b+12,"NOTE OFF.       %s%d (%2d)  (%02d)\n", notes[n0%12], n0/12, n0+1, n1);
    break;
  case AFTERTOUCH:
    sprintf(b+12, "AFTERTOUCH      %s%d (%2d) (%02d)\n", notes[n0%12], n0/12, n0+1, n1);
    break;
  case CONTROLCHANGE:
    sprintf(b+12, "CONTROL         %d=%d\n", n0, n1);
    break;
  case PROGRAMCHANGE:
    sprintf(b+12, "PROGRAM         %d\n", n0+1);
    break;
  case CHANNELPRESSURE:
    sprintf(b+12, "CHANNELPRESSURE %d\n", n0);
    break;
  case PITCHWHEEL:
    sprintf(b+12, "PITCHWHEEL      %d\n", n0);
    break;
  }

  return b;
}


int start_playback() {

  int stat;

  xhourglass_on();
  stat = midifile_start(midifilebuffer, midifilesize, &midifileduration);
  xhourglass_off();
  if (stat == MIDI_PLAYING) {
    if (output & OUTPUT_HBP10GM) {
      hbp10gm_scheduler_flush_buffer();
      hbp10gm_scheduler_reset_ticker();
      hbp10gm_scheduler_set_ticks_per_second(1000);
      hbp10gm_schedule(0x0000ff01, 0, 1);
    }

    if (output & OUTPUT_MIDI) {
      _kernel_swi_regs r;

      r.r[0] = 31;
      _kernel_swi(MIDI_Init, &r, &r);
      free_in_midi_scheduler = 1000;

      r.r[0] = 1000;
      r.r[1] = 1;
      _kernel_swi(MIDI_FastClock, &r, &r);
    }

    playing = PLAYING;
    set_volume(volume);

    poll_midi();
    if (mode == MODE_WIMP) {
      set_icon(ICON_PLAY, 1);
      set_file_position(midifileduration, 1);
      set_file_position(0, 0);
    }
  }

  return MIDI_PLAYING;
}


void stop_playback(int notesoff) {

  if (playing == STOPPED)   return;

  if (notesoff) {
    all_notes_off();
    if (output & OUTPUT_HBP10GM)
      hbp10gm_schedule(0x0000ff01, 0, 1);

    if (output & OUTPUT_MIDI) {
      _kernel_swi_regs r;

      r.r[0] = 31;
      _kernel_swi(MIDI_Init, &r, &r);
      free_in_midi_scheduler = 1000;

      r.r[0] = 0x010000ff;
      r.r[1] = 0;
      _kernel_swi(MIDI_TxCommand, &r, &r);
    }
  }

  if (midifilebuffer)   free(midifilebuffer);
  midifilebuffer = NULL;
  playing = 0;

  if (mode == MODE_WIMP) {
    set_file_title("");
    set_file_position(0, 0);
    set_icon(ICON_PLAY, 0);
    set_icon(ICON_PAUSE, 0);
  }
}


void all_notes_off() {

  if (playing == STOPPED)  return;

  if (output & OUTPUT_HBP10GM) {
    int c;

    hbp10gm_scheduler_flush_buffer();
    hbp10gm_scheduler_reset_ticker();
    for (c = 0; c < 16; c++)
      hbp10gm_schedule(0x007bb003 | (c<<8), 0, c);
  }

  if (output & OUTPUT_MIDI) {
    int c;
    _kernel_swi_regs r;

    r.r[0] = 31;
    _kernel_swi(MIDI_Init, &r, &r);
    free_in_midi_scheduler = 1000;

    for (c = 0; c < 16; c++) {
      r.r[0] = 0x03007bb0 | c;
      r.r[1] = 0;
      _kernel_swi(MIDI_TxCommand, &r, &r);
    }
  }
}


void report_error(os_error *err, int die) {

  xwimp_report_error(err, 1, "PlayMIDI", NULL);
  if (die)  exit(-1);
}


void redraw_play_window(wimp_draw *draw, bool *more) {

  int x0, y0;

  x0 = draw->box.x0 - draw->xscroll;
  y0 = draw->box.y1 - draw->yscroll;

  while (*more) {

    if (draw->clip.x0 < x0 + ICON_VOLUME_X + 68) {
      os_factors factors;
      osspriteop_trans_tab trans;
      double v, dB;

      xwimp_read_pix_trans(osspriteop_USER_AREA, spritearea, (osspriteop_id)"vol",
                           &factors, &trans);
      xosspriteop_put_sprite_scaled(osspriteop_USER_AREA, spritearea, (osspriteop_id)"vol",
                           x0 + ICON_VOLUME_X, y0 + ICON_VOLUME_Y, 8, NULL, &trans);
      xos_plot(os_MOVE_TO, x0 + ICON_VOLUME_X + 33, y0 + ICON_VOLUME_Y + 33);
      if (volume == 0)
        dB = -60.0;
      else
        dB = 20.0*log10(volume) - 60.0;
      v = -(50.0 + 280.0*dB/60.0)/57.295;
      xos_plot(os_PLOT_SOLID | os_PLOT_BY, (int)(24*cos(v)), (int)(24*sin(v)));
    }

    if (draw->clip.x1 > x0 + VU_METER_X) {
      os_factors factors;
      osspriteop_trans_tab trans;

      xwimp_read_pix_trans(osspriteop_USER_AREA, spritearea, (osspriteop_id)"v0",
                           &factors, &trans);
      xosspriteop_put_sprite_scaled(osspriteop_USER_AREA, spritearea, (osspriteop_id)vusprite,
                           x0 + VU_METER_X, y0 + VU_METER_Y, 0, NULL, &trans);
    }

    xwimp_get_rectangle(draw, more);
  }
}


void set_vu_meter(int vu) {

  wimp_draw draw;
  bool more;

  vumeter = vu;
  vu /= 35;
  if (vu > 7)  vu = 7;

  vusprite[0] = 'v';
  vusprite[1] = '0'+vu;
  vusprite[2] = '\0';

  draw.w = main_window;
  draw.box.x0 = VU_METER_X;
  draw.box.y0 = VU_METER_Y;
  draw.box.x1 = VU_METER_X+8;
  draw.box.y1 = VU_METER_X+64;
  xwimp_update_window(&draw, &more);
  redraw_play_window(&draw, &more);
}


void set_volume(int v) {

  if (v < 0)     v = 0;
  if (v > 1024)  v = 1024;
  if ((playing == PLAYING) || (playing == WAITINGFOREND))
    if (output & OUTPUT_HBP10GM)   hbp10gm_master_volume(v);
  volume = v;

  if (mode == MODE_WIMP) {
    wimp_draw draw;
    bool more;

    draw.w = main_window;
    draw.box.x0 = ICON_VOLUME_X+8;
    draw.box.y0 = ICON_VOLUME_Y+8;
    draw.box.x1 = ICON_VOLUME_X+58;
    draw.box.y1 = ICON_VOLUME_Y+58;
    xwimp_update_window(&draw, &more);
    redraw_play_window(&draw, &more);
  }
}


void set_file_position(int ms, int dur) {

  wimp_icon_state icn;
  int i;

  if (dur)
    i = ICON_DURATION;
  else {
    i = ICON_POSITION;
    midifileposition = ms;
  }
  icn.w = main_window;
  icn.i = i;
  xwimp_get_icon_state(&icn);
  ms /= 1000;
  sprintf(icn.icon.data.indirected_text.text, "%02d:%02d", ms/60, ms%60);
  xwimp_set_icon_state(main_window, i, 0, 0);
}


void set_file_title(char *name) {

  wimp_icon_state icn;
  int i;

  for (i = 0; (i < 212) && (name[i] > ' '); i++)  ;
  name[i]  = '\0';

  icn.w = main_window;
  icn.i = ICON_TITLE;
  xwimp_get_icon_state(&icn);
  if (strlen(name) > 36) {
    strcpy(icn.icon.data.indirected_text.text, "...");
    strcpy(icn.icon.data.indirected_text.text+3, name+strlen(name)-36);
  } else {
    strcpy(icn.icon.data.indirected_text.text, name);
  }
  xwimp_set_icon_state(main_window, ICON_TITLE, 0, 0);
}


void set_icon(wimp_i i, int pressed) {

  if (pressed)
    xwimp_set_icon_state(main_window, i, wimp_ICON_SELECTED, wimp_ICON_SELECTED);
  else
    xwimp_set_icon_state(main_window, i, 0, wimp_ICON_SELECTED);
}


void pause_playback() {

  if (playing != PLAYING)  return;
  playing = PAUSED;
  if (output & OUTPUT_HBP10GM)  hbp10gm_master_volume(0);
  set_icon(ICON_PAUSE, 1);
  midifile_pause(1, 0);
}


void unpause_playback() {

  if (playing != PAUSED)  return;
  playing = PLAYING;
  if (output & OUTPUT_HBP10GM)  hbp10gm_master_volume(volume);
  set_icon(ICON_PAUSE, 0);
  midifile_pause(0, 0);
}
